/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cn.yunyichina.provider.framework.annotation.validate;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @author jiangtch
 */
public class Validator {

    private static final Logger logger = LoggerFactory.getLogger(Validator.class);

    public final static String SUFFIX = "Handler";

    public final static String PREFIX = "Validate";

    private static Validator validator = new Validator();

    private Validator() {
    }

    /**
     * get the Validator object
     *
     * @return Validator object
     */
    public static Validator getInstance() {
        return validator;
    }

    public void validate(AnnotationValidable validatedObj)
            throws ValidateException {
        if (null == validatedObj) {
            logger.warn("The input validatedObj is null.");
            return;
        }
        if (logger.isTraceEnabled()) {
            logger.trace(this.getClass().getName(), "validate()");
        }
        Class currentClass = validatedObj.getClass();
        while (currentClass != null) {
            Field[] fields = currentClass.getDeclaredFields();
            for (Field elem : fields) {
                validateField(validatedObj, elem);
            }

            Class superClass = currentClass.getSuperclass();
            currentClass = AnnotationValidable.class.isAssignableFrom(superClass) ? superClass : null;
        }
    }

    private void validateField(AnnotationValidable validatedObj, Field field) throws ValidateException {
        if (AnnotationValidable.class.isAssignableFrom(field.getType())) {
            Object destValue;
            try {
                destValue = GetFiledValue.getField(validatedObj, field.getName());
            } catch (Exception ex) {
                logger.error("Get field value or cast value error for field "
                                + field.getName()
                                + " in Class "
                                + validatedObj.getClass()
                                + ". Error message: "
                                + ex.getMessage(),
                        ex);
                throw new ValidateException("Get field value or cast value error for field "
                        + field.getName()
                        + " in Class "
                        + validatedObj.getClass()
                        + ". Error message: "
                        + ex.getMessage(), ex);
            }

            if (destValue == null) {
                return;
            } else {
                validate((AnnotationValidable) destValue);
            }
        }

        List<Annotation> annotations = getValidateAnnotations(field);
        if (annotations != null && annotations.size() > 0) {
            for (Annotation annotation : annotations) {
                String annotationName = annotation.annotationType().getName();
                String className = annotationName + SUFFIX;
                Handler handler;
                try {
                    handler = (Handler) Class.forName(className).newInstance();
                } catch (Exception ex) {
                    logger.error("Get validate handler error. Error message: " + ex.getMessage(), ex);
                    throw new ValidateException("Can not get the handler for " + ex.getMessage(), ex);
                }
                handler.validate(validatedObj, field);
            }
        }
    }

    private List<Annotation> getValidateAnnotations(Field field) {
        List<Annotation> annotations = new ArrayList<>();
        Annotation[] annos = field.getAnnotations();
        for (Annotation elem : annos) {
            if (elem.annotationType().getSimpleName().startsWith(PREFIX)) {
                annotations.add(elem);
            }
        }
        return annotations;
    }

}
